<template>
  <div
    ref="dragBoxRef"
    class="img-modal"
    @mousedown="startDragging"
    @mousemove="handleDrag"
    @mouseup="stopDragging"
    :style="boxStyle"
  >
    <div class="modal-content">
      <img
        class="preview-img"
        ref="img"
        draggable="false"
        :src="imgSrc"
        @load="onImgLoad"
        :style="{
          zoom: scaleRate
        }"
      />
    </div>
    <div class="imgs-box">
      <img
        class="icon close-icon"
        src="@/assets/images/image-scale/close.svg?url"
        @click="close"
      />
      <img
        class="icon"
        @click="bigger"
        src="@/assets/images/comment/enlarge.png"
      />
      <img
        class="icon"
        @click="smaller"
        src="@/assets/images/comment/reduce.png"
      />
      <span class="icon" @click="scaleRate = 1"> 1:1 </span>
    </div>
  </div>
</template>

<script setup>
import { ref, computed, reactive } from 'vue'

const props = defineProps({
  imgSrc: String
})

const emit = defineEmits(['close'])

function close() {
  scaleRate.value = 1
  emit('close')
}

const getParentBounds = computed(() => {
  const parent = dragBoxRef.value.parentNode
  return {
    width: parent.offsetWidth,
    height: parent.offsetHeight
  }
})

const img = ref()
function onImgLoad(e) {
  const targetImg = e.target
  const { naturalWidth, naturalHeight } = targetImg
  const { width, height } =
    targetImg.parentElement.parentElement.getBoundingClientRect()
  scaleRate.value = Number(
    Math.min(width / naturalWidth, height / naturalHeight).toFixed(1)
  )
  img.value.style.opacity = '1'
  img.value.style.transition = ''
  dragBox.translateX = (getParentBounds.value.width - img.value.offsetWidth) / 2
  dragBox.translateY =
    (getParentBounds.value.height - img.value.offsetHeight) / 2
}

/* 缩放与拖动 */

const scaleRate = ref(1)

function bigger() {
  scaleRate.value = Number((scaleRate.value + 0.2).toFixed(1))
}

function smaller() {
  if (scaleRate.value <= 0.5) {
    scaleRate.value = 0.5
    return
  }
  scaleRate.value = Number((scaleRate.value - 0.1).toFixed(1))
}

// 拖拽
const dragBox = reactive({
  dragging: false,
  offsetX: 0,
  offsetY: 0,
  translateX: 0,
  translateY: 0
})
const dragBoxRef = ref()

const startDragging = e => {
  dragBox.dragging = true
  dragBox.offsetX = e.pageX - dragBox.translateX
  dragBox.offsetY = e.pageY - dragBox.translateY
}

const stopDragging = () => {
  dragBox.dragging = false
}

const isWithinBounds = (x, y) => {
  const maxX = getParentBounds.value.width - dragBoxRef.value.offsetWidth
  const maxY = getParentBounds.value.height - dragBoxRef.value.offsetHeight

  return x >= 0 && x <= maxX && y >= 0 && y <= maxY
}

const handleDrag = e => {
  if (dragBox.dragging) {
    requestAnimationFrame(() => {
      const translateX = e.pageX - dragBox.offsetX
      const translateY = e.pageY - dragBox.offsetY

      if (isWithinBounds(translateX, translateY)) {
        dragBox.translateX = translateX
        dragBox.translateY = translateY
      }
    })
  }
}

const boxStyle = computed(() => {
  const transitionStyle =
    dragBox.translateX === 0 && dragBox.translateY === 0
      ? { transition: '.12s' }
      : {}

  return {
    transform: `translate(${dragBox.translateX}px,${dragBox.translateY}px)`,
    ...transitionStyle
  }
})
</script>

<style scoped lang="less">
.img-modal {
  will-change: transform;
  position: absolute;
  max-width: 80vw;
  max-height: 80vh;
  cursor: move;

  .imgs-box {
    padding: 10px;
    position: absolute;
    display: flex;
    flex-direction: column;
    gap: 20px;
    right: -50px;
    top: 0;
    background-color: rgba(0, 0, 0, 0.4);
    border-radius: 0 5px 5px 0;
    text-align: center;
    color: #fff;
  }

  * {
    user-select: none;
  }

  .modal-content {
    background-color: #fff;
    width: 100%;
    box-sizing: border-box;
    overflow: auto;
    display: grid;
    place-items: center;
    position: relative;
  }

  .icon {
    cursor: pointer;
    width: 28px;

    &.close-icon {
      width: 22px;
    }
  }
}
</style>
